/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 
// 															  
//             Copyright(c) 2024 XLX
//	              All rights reserved.	
// XLX:   Kenglee						
// Time: 2024-8-21        			 
// File name:		xlx_tcpsocket.c	               				
// Software version: 	1.0							
// Function:  	SOCKET
 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/


#include <stdlib.h>
#include <string.h>

#include "xlx_core.h"
#include "xlx_platform.h"
#include "xlx_queue.h"
#include "xlx_socket.h"
#include "xlx_tcpsocket.h"

static const char* TAG = "TcpSct";

typedef struct{
    char domain[64];//存储域名
    unsigned long ip_addr;//存储解析后的ip
    char loaded;//文件已经加载过
    int step;//记录ip使用流程
}TutuServerInfo;

typedef struct{
    TutuSocketHandle sock;//连接对象
    char connected;//1:连接 0：未连接
    unsigned long addr;//地址
    int port;//端口号
    TutuTcpCb cb;//事件通知
}TutuTcpHandle;

typedef struct{
    char *ptr;
    int len;//ptr内容长度
    int rpos;//读到第几个字节
}TutuTcpDataPkg;

typedef struct{
    int wpos;//当前队里写到第几个元素
    int rpos;//当前队列读到第几个元素
    int size;//当前存储数量
    int max;//最大存储数据量
    TutuTcpDataPkg list[256];
}TutuTcpRing;

static void tututcp_close(void);
static void tututcp_release_res(void);

//--------------变量定义-------------------------
//用来缓存域名解析的ip
static TutuServerInfo g_pri_serverinfo_cfg = {0};

//网络对象
static TutuTcpHandle g_pri_tcp_handle = {0};

//消息队列
static TutuQueueHandle g_pri_tcp_queue = NULL;

//环形存储,数据包发送队列
//专门用来存储待发送的声控报警包
static TutuTcpRing g_pri_tcp_audio_ring = {0};
//专门用来存储待发送的指令和交互反馈数据包
static TutuTcpRing g_pri_tcp_normal_ring = {0};
//掉线后存储备份数据
static TutuTcpRing g_pri_tcp_offline_ring = {0};
//强制发送数据包的通道
static TutuTcpRing g_pri_tcp_force_ring = {0};
//用来切换，使用audio的还是normal
static char g_pri_tcp_ring_switch = 0;
//是否允许发送数据包
static char g_pri_tcp_allow_writing = 0;
//-----------------------------------------------

static TutuThreadHandle * tutu_tcp_service_task_thread;

//放一个数据包到队列中
char tututcp_ring_put(TutuTcpRing *ring,char *data,int len){

    if(ring->size + 1 <= ring->max){
        ring->list[ring->wpos].ptr = data;
        ring->list[ring->wpos].len = len;
        ring->list[ring->wpos].rpos = 0;
        ring->wpos++;
        ring->size++;
        ring->wpos = ring->wpos % ring->max;

        return 1;
    }
    return 0;
}

//从队列中取出一个数据包
TutuTcpDataPkg *tututcp_ring_pop(TutuTcpRing *ring){
    TutuTcpDataPkg *ret = NULL;
    if(ring->size - 1 >= 0){
        ret = &(ring->list[ring->rpos]);
        ring->rpos++;
        ring->rpos = ring->rpos % ring->max;
        ring->size--;
    }

    return ret;
}

//得到队列中第一个数据包的地址
TutuTcpDataPkg *tututcp_ring_first(TutuTcpRing *ring){
    TutuTcpDataPkg *ret = NULL;
    if(ring->size > 0){
        ret = &(ring->list[ring->rpos]);
    }
    return ret;
}


//保存配置文件
static void tututcp_save_domain_cfg(){
    g_tutufile.write_file(TUTU_DOMAIN_FILE_NAME,(char*)&g_pri_serverinfo_cfg,sizeof(TutuServerInfo));
}


//加载配置文件
static void tututcp_load_domain_cfg(){
    const char * file = TUTU_DOMAIN_FILE_NAME;
    unsigned long int len = sizeof(TutuServerInfo);
    if(g_tutufile.get_file_size(file) == len){
        g_tutufile.read_file(file,(char*)&g_pri_serverinfo_cfg,&len);
    }else{
        memset(&g_pri_serverinfo_cfg,0,len);
    }
}


// //设置服务器地址
// static void tututcp_setaddr(const char *szaddr,int port){

//     unsigned long socket_addr = 0;

// 	struct sockaddr_in server;
// 	struct hostent* hostname;

//     g_pri_tcp_handle.port = port;

//     //如果配置文件还没有加载过，则加载配置文件
//     if(g_pri_serverinfo_cfg.loaded == 0){
//         tututcp_load_domain_cfg();
//         g_pri_serverinfo_cfg.step = 1;
//         g_pri_serverinfo_cfg.loaded = 1;//标记文件已经加载
//     }

//     //使用上次解析保存好的ip地址
//     if(g_pri_serverinfo_cfg.ip_addr > 0){
//         g_pri_serverinfo_cfg.step = 2;
//         //如果和之前缓存的域名相同，则直接使用之前解析过的ip
//         if(strcmp(g_pri_serverinfo_cfg.domain,szaddr) == 0){
//             g_pri_tcp_handle.addr =  g_pri_serverinfo_cfg.ip_addr;
//             return;
//         }
//     }


//     //走域名解析流程
//     g_pri_serverinfo_cfg.step = 3;
// 	hostname = gethostbyname(szaddr);
// 	if(hostname != NULL && hostname->h_addr_list[0])
// 	{
// 		server.sin_family = 2;
// 	    server.sin_addr.s_addr= * (unsigned int *) hostname->h_addr_list[0];
// 		socket_addr = server.sin_addr.s_addr;

// 		TUTULOG("%s %d  dns:%s %d.%d.%d.%d success",
//         __func__,__LINE__,szaddr,
//         (socket_addr & 0xff),
//         (socket_addr>>8)&0xff,
//         (socket_addr >> 16)&0xff,
//         (socket_addr>>24)
//         );

// 		g_pri_serverinfo_cfg.ip_addr = g_pri_tcp_handle.addr =  socket_addr;
//         if(strlen(szaddr) < 64){
//             //保存域名解析后的ip到文件,缓存起来
//             strcpy(g_pri_serverinfo_cfg.domain,szaddr);
//             tututcp_save_domain_cfg();
//         }
//         return;
// 	}else{
//         memset(&g_pri_serverinfo_cfg,0,sizeof(TutuServerInfo));
//     }

// 	TUTULOG("%s %d	error",__func__,__LINE__);
// }


//连接成功or失败
static void tututcp_event_connect_status(TutuSocketHandle handle,int success){
    g_pri_tcp_handle.connected = success;
    g_pri_tcp_handle.sock = handle;

    
    if(success == 1){
        //连接服务器成功
        if(g_pri_tcp_handle.cb.on_connect_success){
            g_pri_tcp_handle.cb.on_connect_success();
        }
    }else{
        //连接服务器失败
        if(g_pri_tcp_handle.cb.on_disconnect){
            g_pri_tcp_handle.cb.on_disconnect();
        }
    }
}

//有新数据来了
static void tututcp_event_has_new_data(TutuSocketHandle handle,char *data,int len){
    if(g_pri_tcp_handle.cb.on_recv_data){
        g_pri_tcp_handle.cb.on_recv_data(data,len);
    }
}

//连接出现错误
static void tututcp_event_connect_error(TutuSocketHandle handle,int ecode){
    g_pri_tcp_handle.connected = 0;
}

//通知连接已经被关闭
static void tututcp_event_connect_close(TutuSocketHandle handle){
    g_pri_tcp_handle.connected = 0;
    if(g_pri_tcp_handle.cb.on_disconnect){
        g_pri_tcp_handle.cb.on_disconnect();
    }
    tututcp_release_res();
}

//是否有数据包需要发送
static int tututcp_event_need_send_data(TutuSocketHandle handle){
    if(g_pri_tcp_audio_ring.size > 0 || g_pri_tcp_normal_ring.size > 0 || g_pri_tcp_force_ring.size > 0){
        //如果队列里面有数据，则需要发送数据到服务器
        return 1;
    }
    return 0;
}

//可以发送数据包
static void tututcp_event_can_send_data(TutuSocketHandle handle){
    TutuTcpHandle *p = &g_pri_tcp_handle;
    TutuTcpDataPkg *pkg = NULL;
    TutuTcpRing *ring = NULL;
    int wsize = 0;


    if(p->connected != 1){
        return;
    }

    //需要强制发送的数据，优先级最高
    if(g_pri_tcp_force_ring.size > 0){
        ring = &g_pri_tcp_force_ring;
        pkg = tututcp_ring_first(ring);
    }

    //要允许，才能发送其他数据
    if(pkg == NULL && g_pri_tcp_allow_writing == 1){


        //使用这个变量，控制交替发送数据包
        if(g_pri_tcp_ring_switch == 1){
            g_pri_tcp_ring_switch = 0;
        }else{
            g_pri_tcp_ring_switch = 1;
        }


        //发送规则：(声控报警数据、普通数据)
        //都有数据的情况下，则按照变量切换，交替发送
        //如果存在对方无数据的情况下，则谁有数据，发谁

        if(g_pri_tcp_ring_switch == 1 && g_pri_tcp_audio_ring.size > 0){
            //如果轮到声控报警数据发送，并且有数据发送，则发送声控报警数据
            ring = &g_pri_tcp_audio_ring;
            pkg = tututcp_ring_first(ring);
            //g_tutuos.sleep2(100);//增加间歇，防止声控报警数据过快，把带宽占满
            //增加休眠会延长网络时间，导致功耗变大，所以注释掉
        }else{
            //否则发送普通数据
            ring = &g_pri_tcp_normal_ring;
            pkg = tututcp_ring_first(ring);
        }

        //如果普通数据也为空，但是声控报警数据有，则发送声控报警数据
        if(pkg == NULL && g_pri_tcp_audio_ring.size > 0){
            ring = &g_pri_tcp_audio_ring;
            pkg = tututcp_ring_first(ring);
            //g_tutuos.sleep2(100);//增加间歇，防止声控报警数据过快，把带宽占满
            //增加休眠会延长网络时间，导致功耗变大，所以注释掉
        }
    }

    

    //这里要从队列里面取出数据，然后发送。
    //如果发送失败，则直接关闭连接，并保存没发送成功的数据包
    if(pkg != NULL){
        wsize = g_tutusocket.socket_write(
            p->sock,
            pkg->ptr + pkg->rpos,
            pkg->len - pkg->rpos
        );

        if(wsize == pkg->len){
            //有机会完整发送出去
            goto del_ring;
        }else{
            int b = pkg->len - wsize;
            if(b > 0){
                pkg->rpos = b;//计算已经发出去多少数据
                return;
            }
        }
    }

del_ring:
    if(pkg != NULL){
        if(ring != NULL){
            tututcp_ring_pop(ring);
        }
        TUTUFree(pkg->ptr);
        pkg->ptr = NULL;
        pkg->len = 0;
        pkg->rpos = 0;
    }
}


//连接服务器
static void tututcp_connect(const char *szaddr,int port){
    char ipbuf[32]={0};
    TutuSocketEvent event;
    event.event_can_send_data = tututcp_event_can_send_data;
    event.event_connect_close = tututcp_event_connect_close;
    event.event_connect_error = tututcp_event_connect_error;
    event.event_connect_status = tututcp_event_connect_status;
    event.event_has_new_data = tututcp_event_has_new_data;
    event.event_need_send_data = tututcp_event_need_send_data;

    if(1 == g_tutuos.getaddress(szaddr,ipbuf,32)){
        TUTULOG("dns domain=%s ip=%s",szaddr,ipbuf);
        g_tutusocket.tcp_connect(ipbuf,port,&event);
    }else{
        TUTULOG("dns parse failt %s!!!!!",szaddr);

        g_pri_tcp_handle.connected = 0;
        if(g_pri_tcp_handle.cb.on_disconnect){
            g_pri_tcp_handle.cb.on_disconnect();
        }
    }
}


//关闭连接
static void tututcp_close(void){
    g_tutusocket.socket_close(g_pri_tcp_handle.sock);
}

//释放资源
static void tututcp_release_res(void){
    TutuTcpDataPkg *p = 0;

    g_pri_tcp_allow_writing = 0;
    g_pri_tcp_handle.sock = 0;
    g_pri_tcp_handle.connected = 0;

    g_pri_serverinfo_cfg.ip_addr = 0;
    g_pri_serverinfo_cfg.step = 0;
    tututcp_save_domain_cfg();


    //连接关闭，如果之前队列中还有没发送完的数据，则通知用户，是否需要保存下来
    while(g_pri_tcp_normal_ring.size > 0){
        p = tututcp_ring_pop(&g_pri_tcp_normal_ring);
        if(p){
            if(g_pri_tcp_handle.cb.on_filter_pkg_tobackup && 1 == g_pri_tcp_handle.cb.on_filter_pkg_tobackup(p->ptr,p->len)){
                //如果用户需要放入离线包，则放入离线包存取区
                tututcp_ring_put(&g_pri_tcp_offline_ring,p->ptr,p->len);
            }else{
                //如果不需要存储的，则直接释放
                TUTUFree(p->ptr);
                p->ptr = 0;
                p->len = 0;
            }
        }
    }
}

//是否连接上服务器
static char tututcp_is_connect(){
    return g_pri_tcp_handle.connected;
}

//发送普通数据消息
//把数据写入发送队列
//1:写入成功 0：写入失败
static char tututcp_postmsg_write(const char *szdata,int len){
    if(g_pri_tcp_normal_ring.size + 1 < g_pri_tcp_normal_ring.max*0.8){
        TutuQueueMsg  sndMsg = {0};
        sndMsg.msg_id = 2;
        sndMsg.data_len = len;
        sndMsg.data = TUTUAlloc(len);
        memcpy(sndMsg.data,szdata,len);
        return g_tutuqueue.isend(g_pri_tcp_queue,&sndMsg);
    }

    return 0;
}

//发送声控报警数据消息
static char tututcp_postmsg_write_audio(const char *szdata,int len){
    if(g_pri_tcp_audio_ring.size + 1 < g_pri_tcp_audio_ring.max*0.8){
        TutuQueueMsg  sndMsg = {0};
        sndMsg.msg_id = 5;
        sndMsg.data_len = len;
        sndMsg.data = TUTUAlloc(len);
        memcpy(sndMsg.data,szdata,len);
        return g_tutuqueue.isend(g_pri_tcp_queue,&sndMsg);
    }
    return 0;
}

static int tututcp_msg_audio_count(){
    return g_tutuqueue.iCount(g_pri_tcp_queue);
}

static int tututcp_audio_queue_count(){
    return g_pri_tcp_audio_ring.size;
}

//发送消息，关闭连接
static void tututcp_postmsg_close(){
    TutuQueueMsg  sndMsg = {0};
    sndMsg.msg_id = 4;
    g_tutuqueue.isend(g_pri_tcp_queue,&sndMsg);
}


static char g_pri_tcp_addrbuf[64] = {0};
static int g_pri_tcp_port = 0;
//发送消息连接服务器
static void tututcp_postmsg_connect(const char *szaddr, int port, TutuTcpCb *cb){
    TutuQueueMsg  sndMsg = {0};

    if(strlen(szaddr) < 64){
        g_pri_tcp_port = port;
        strcpy(g_pri_tcp_addrbuf,szaddr);
        g_pri_tcp_handle.cb.on_connect_success = cb->on_connect_success;
        g_pri_tcp_handle.cb.on_disconnect = cb->on_disconnect;
        g_pri_tcp_handle.cb.on_recv_data = cb->on_recv_data;
        g_pri_tcp_handle.cb.on_filter_pkg_tobackup = cb->on_filter_pkg_tobackup;

        sndMsg.msg_id = 3;
        g_tutuqueue.isend(g_pri_tcp_queue,&sndMsg);
    }else{
        TUTULOG("%s %d szaddr length > 64 !!!!",__func__,__LINE__);
    }
}

//发送消息，加载离线数据
static void tututcp_postmsg_loadofflinedata(){
    TutuQueueMsg  sndMsg = {0};
    sndMsg.msg_id = 6;
    g_tutuqueue.isend(g_pri_tcp_queue,&sndMsg);
}

//加载离线数据
static void tututcp_load_offline_data(){
    TutuTcpDataPkg *p = 0;
    while(g_pri_tcp_offline_ring.size > 0){
        p = tututcp_ring_pop(&g_pri_tcp_offline_ring);

        if(p){
            tututcp_ring_put(&g_pri_tcp_normal_ring,p->ptr,p->len);
        }
    }
}

static void tututcp_soket_loop(){
    TutuQueueMsg  sndMsg = {0};
    g_tutusocket.socket_loop();

    if(tututcp_is_connect() == 1){
        sndMsg.msg_id = 11;
        sndMsg.data_len = 0;
        sndMsg.data = NULL;
        g_tutuqueue.isend(g_pri_tcp_queue,&sndMsg);
    }
    
}

//tcp 服务
void tututcp_service_task(void *params){
    char code = 0;
    TutuQueueMsg recvMsg = {0};
    TUTU_LOGI(TAG,"Tcp service task is runnging...(%s, %d)", __FUNCTION__, __LINE__);
    while(1){  

        //消息循环,实际测试是1秒(根据time.tv_sec这个时间)循环一次(当网络注册正常后)
        code = g_tutuqueue.irecv(g_pri_tcp_queue, &recvMsg, 30); // 30秒超时
        //code = g_tutuqueue.irecv(g_pri_tcp_queue, &recvMsg, osWaitForever);
        // TUTU_LOGI(TAG,"recvMsg.id= %d(%s, %d)", recvMsg.msg_id, __FUNCTION__, __LINE__);
        if(code == 1){
            switch (recvMsg.msg_id)
            {
            case 2://把数据加入发送队列 - 普通数据
                tututcp_ring_put(&g_pri_tcp_normal_ring,recvMsg.data,recvMsg.data_len);
                break;
            case 5://把数据加入发送队列 - 声控报警数据
                tututcp_ring_put(&g_pri_tcp_audio_ring,recvMsg.data,recvMsg.data_len);
                break;
            case 3://连接服务器
                tututcp_connect(g_pri_tcp_addrbuf,g_pri_tcp_port);
                tututcp_soket_loop();
                continue;
            case 4://关闭连接
                tututcp_close();
                break;
            case 6://加载离线数据
                tututcp_load_offline_data();
                break;
            case 7://把数据加入发送队列 - 强制发送数据
                tututcp_ring_put(&g_pri_tcp_force_ring,recvMsg.data,recvMsg.data_len);
                break;
            case 11:
                tututcp_soket_loop();
#if 0
// 打印波形，可以用示波器查看
                {
                    static unsigned char swap_io = 0;
                    tutu_gpio_set_level(TEST_WAVE_OUT_GPIO, swap_io);
                    swap_io = !swap_io;
                }
#endif                
                break;
            default:
                break;
            }
        }
        
        
    }
}

//是否允许发送数据包到服务器，1：允许，0：不允许
static void tututcp_allow_writing(char allow){
    g_pri_tcp_allow_writing = allow;
}

//不需要允许，可强制发送的数据
static char tututcp_force_write(const char *szdata,int len){
    if(g_pri_tcp_force_ring.size + 1 < g_pri_tcp_force_ring.max*0.8){
        TutuQueueMsg  sndMsg = {0};
        sndMsg.msg_id = 7;
        sndMsg.data_len = len;
        sndMsg.data = TUTUAlloc(len);
        memcpy(sndMsg.data,szdata,len);
        return g_tutuqueue.isend(g_pri_tcp_queue,&sndMsg);
    }

    return 0;
}

void tututcp_socket_close(void){
    tututcp_close();
}

void TutuTcpSocket_Init(TutuTcpSocket *myself){
    g_pri_tcp_handle.sock = 0;

    //初始化最大容量
    g_pri_tcp_offline_ring.max = g_pri_tcp_force_ring.max = g_pri_tcp_normal_ring.max = g_pri_tcp_audio_ring.max = sizeof(g_pri_tcp_audio_ring.list)/sizeof(TutuTcpDataPkg);    

    myself->connect_server = tututcp_postmsg_connect;
    myself->write = tututcp_postmsg_write;
    myself->write_audio = tututcp_postmsg_write_audio;
    myself->audio_msg_count = tututcp_msg_audio_count;
    myself->audio_queue_count = tututcp_audio_queue_count;
    myself->load_offline_data = tututcp_postmsg_loadofflinedata;
    myself->close_server = tututcp_postmsg_close;
    myself->is_connect = tututcp_is_connect;
    myself->allow_writing = tututcp_allow_writing;
    myself->force_write = tututcp_force_write;
    
    g_tutuqueue.icreate(&g_pri_tcp_queue,32);
    tutu_tcp_service_task_thread = g_tututhread.new_thread(1024*8, "TcpSrv", 8, tututcp_service_task);
    TUTULOG("sys_init: %s(%d)", __FUNCTION__, __LINE__);
}
